iT邦幫忙

2024 iThome 鐵人賽

DAY 28
0
Mobile Development

從 SwiftUI 到 Apple Vision Pro - SwiftUI 從零開始系列 第 28

從 SwiftUI 到 Apple Vision Pro - SwiftUI 從零開始 Day28

  • 分享至 

  • xImage
  •  

Day28 可拖曳的立方體

自動生成的立方體,也可以使用拖曳方式,拖曳到空間的任何位置。

使用Entity來建立一個容器:

private var contentEntity = Entity()
    
func setupContentEntity() -> Entity {
    return contentEntity
}

生成一個立方體:

func addCube() -> Entity {
    let entity = ModelEntity(
        mesh: .generateBox(size: 0.5, cornerRadius: 0),
        materials: [SimpleMaterial(color: .red, isMetallic: false)],
        collisionShape: .generateBox(size: SIMD3<Float>(repeating: 0.5)),
        mass: 0.0
    )
        
    entity.components.set(InputTargetComponent(allowedInputTypes: .indirect))
    entity.position = SIMD3(x: 0, y: 1, z: -2)
    
    contentEntity.addChild(entity)
        
    return entity
}

程式碼逐一說明:

mesh: .generateBox(size: 0.5, cornerRadius: 0):產生一個大小為0.5的立方體。

materials: [SimpleMaterial(color: .red, isMetallic: false)]:這個立方體具有紅色的表面,材質為非金屬。

collisionShape: .generateBox(size: SIMD3(repeating: 0.5)):這個立方體具有碰撞的功能,必須要指定這個碰撞,才可以進行拖曳。

mass: 0.0:質量為0,表示不會受到重心影響,如果有設定大於0,立方體就會自動往下掉。

回到ImmersiveView,加入這個立方體:

@StateObject var model = Day28ViewModel()
@State var cube = Entity()

將立方體加入到場景當中:

RealityView { content in
    content.add(model.setupContentEntity())
    cube = model.addCube()
}

加入拖曳指令:

RealityView { content in
    content.add(model.setupContentEntity())
    cube = model.addCube()
}
.gesture(
    DragGesture()
        .targetedToEntity(cube)
        .onChanged { value in
            cube.position = value.convert(value.location3D, from: .local, to: cube.parent!)
        }
    )

與iOS系統的拖曳指令最大差別在於.targetedToEntity(cube):將手勢與指定的實體進行綁定。所以這個拖曳動作只會作用於這個立方體。

value.location3D:取得手勢在空間中的位置。

cube.position = value.convert(value.location3D, from: .local, to: cube.parent!):將立方體的位置設定為目前拖曳空間的新位置。

value.convert:主要是將手勢的局部座標轉換到立方體所在的父實體的座標系。

所以開啟App呈現如圖:

https://ithelp.ithome.com.tw/upload/images/20240828/20162607HJ48EEaj7z.png

打開開關之後,會出現一個紅色立方體:

https://ithelp.ithome.com.tw/upload/images/20240828/20162607FEBbz1BR1n.png

可以滑動到任何位置:

https://ithelp.ithome.com.tw/upload/images/20240828/20162607fUd82QSoM2.png

以下附上完整程式碼。

程式進入點:

import SwiftUI

@main
struct TestVision3App: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
        }

        ImmersiveSpace(id: "ImmersiveSpace") {
            ImmersiveView()
        }
    }
}

視窗View:

import SwiftUI
import RealityKit
import RealityKitContent

struct ContentView: View {
    
    @State private var showImmersiveSpace = false
    
    @Environment(\.openImmersiveSpace) var openImmersiveSpace
    @Environment(\.dismissImmersiveSpace) var dismissImmersiveSpace
    
    var body: some View {
        VStack {
            Toggle("Show ImmersiveSpace", isOn: $showImmersiveSpace)
                .font(.title)
                .frame(width: 360)
                .padding(24)
                .glassBackgroundEffect()
        }
        .padding()
        .onChange(of: showImmersiveSpace) { _, newValue in
            Task {
                if newValue {
                    await openImmersiveSpace(id: "ImmersiveSpace")
                }
                else {
                    await dismissImmersiveSpace()
                }
            }
        }
    }
}

場景View與模型:

import SwiftUI
import RealityKit
import RealityKitContent

struct ImmersiveView: View {
    
    @StateObject var model = Day28ViewModel()
    @State var cube = Entity()
    
    var body: some View {
        RealityView { content in
            content.add(model.setupContentEntity())
            cube = model.addCube()
        }
        .gesture(
            DragGesture()
                .targetedToEntity(cube)
                .onChanged { value in
                    cube.position = value.convert(value.location3D, from: .local, to: cube.parent!)
                }
            )
    }
}

@MainActor class Day28ViewModel: ObservableObject {
    
    private var contentEntity = Entity()
        
    func setupContentEntity() -> Entity {
        return contentEntity
    }
    
    func addCube() -> Entity {
        let entity = ModelEntity(
            mesh: .generateBox(size: 0.5, cornerRadius: 0),
            materials: [SimpleMaterial(color: .red, isMetallic: false)],
            collisionShape: .generateBox(size: SIMD3<Float>(repeating: 0.5)),
            mass: 0.0
        )
            
        entity.components.set(InputTargetComponent(allowedInputTypes: .indirect))
        entity.position = SIMD3(x: 0, y: 1, z: -2)
        
        contentEntity.addChild(entity)
            
        return entity
    }
}

從 SwiftUI 到 Apple Vision Pro - SwiftUI 從零開始 Day28 [完]


上一篇
從 SwiftUI 到 Apple Vision Pro - SwiftUI 從零開始 Day27
下一篇
從 SwiftUI 到 Apple Vision Pro - SwiftUI 從零開始 Day29
系列文
從 SwiftUI 到 Apple Vision Pro - SwiftUI 從零開始30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言